package core.treino;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import view.Jogador;
import core.exception.GeneticoException;
import core.genetico.OperadorGenetico;
import core.jogadores.JogadorAleatorio;
import core.jogadores.JogadorMutante;
import core.jogadores.JogadorMutanteArvore;
import core.jogo.Tabuleiro;
import core.redeNeural.RedeNeural;

public class Treinador {
	
	private final int NUMMAXJOGADAS = 100;
	private final int NUMGRUPOS = 10;
	private final int VALVITORIA = 1;
	private final int VALDERROTA = -2;
	private List<List<RedeNeural>> grupos = new ArrayList<List<RedeNeural>>();
	private Random aleatorio;
	
	public Treinador(){
		int i;
		for(i=0; i < NUMGRUPOS; i++)
			grupos.add(new ArrayList<RedeNeural>());
		aleatorio = new Random();
	}
	
	public List<RedeNeural> evoluirPopulacao(List<RedeNeural> populacao) throws GeneticoException{
		int i, j, redesorteada, resultado;
		List<RedeNeural> novaPopulacao = new ArrayList<RedeNeural>();
		List<RedeNeural> novaPopulacaoAux = new ArrayList<RedeNeural>();
		List<RedeNeural> mutacoes = new ArrayList<RedeNeural>();
		
		for(i=0; i < NUMGRUPOS; i++)
			grupos.get(i).clear();
		
		for(RedeNeural rede : populacao)
			rede.setFitness(0);
		
		
		for(i=0; populacao.size() > 0; i++){
			redesorteada = aleatorio.nextInt(populacao.size());
			grupos.get(i%NUMGRUPOS).add(populacao.remove(redesorteada));
		}
		
		for(List<RedeNeural> grupo : grupos){
			for(i = 0; i < grupo.size(); i++){
				for(j=i+1; j<grupo.size(); j++){					
					resultado = resultadoJogo(grupo.get(i), grupo.get(j));
					if(resultado == 1){
						grupo.get(i).incrementaFitness(VALVITORIA);
						grupo.get(j).incrementaFitness(VALDERROTA);
					}
					if(resultado == -1){
						grupo.get(i).incrementaFitness(VALDERROTA);
						grupo.get(j).incrementaFitness(VALVITORIA);
					}
				}
			}
		}
		
		for(List<RedeNeural> grupo : grupos){
			populacao.addAll(grupo);
		}
		
		Collections.sort(populacao);
		
		//Os 33% melhores pais são conservados e reproduzem
		int numeliminados= 2*populacao.size()/3;
		for(i=0; i<numeliminados; i++)
			populacao.remove(0);
		novaPopulacao = populacao;
		novaPopulacaoAux.addAll(novaPopulacao);
	
		//Adiciona as mutações dos melhores pais
		for(RedeNeural rede : novaPopulacao){			
			mutacoes.add(new RedeNeural(OperadorGenetico.mutacao(rede.getCodigo())));
		}
		novaPopulacao.addAll(mutacoes);		
		
		//Adiciona os filhos
		String[] codigos;
		int sort1, sort2;
		RedeNeural ret1, ret2;
		while(novaPopulacaoAux.size() > 1){
			sort1 = aleatorio.nextInt(novaPopulacaoAux.size());
			sort2 = 0;
			while(sort2 == sort1)
				sort2 = aleatorio.nextInt(novaPopulacaoAux.size());
			ret1 = novaPopulacaoAux.get(sort1);
			ret2 = novaPopulacaoAux.get(sort2);
			codigos = OperadorGenetico.crossover(ret1.getCodigo(), ret2.getCodigo());
			novaPopulacao.add(new RedeNeural(codigos[0]));
			novaPopulacao.add(new RedeNeural(codigos[1]));
			novaPopulacaoAux.remove(ret1);
			novaPopulacaoAux.remove(ret2);
		}		
		
		return novaPopulacao;
	}
	
	/**
	 * Faz duas funções de avaliação se enfrentarem
	 * 1 é vitória da rede 1
	 * 0 é empate
	 * -1 é vitória da rede 2
	 * @param rede1
	 * @param rede2
	 * @return
	 */
	public int resultadoJogo(RedeNeural rede1, RedeNeural rede2){
		int i;
		Tabuleiro tabuleiro = new Tabuleiro();
		int[] casas = tabuleiro.getCasas();
		Jogador jogador1, jogador2;
		if(rede1 != null)
			jogador1 = new JogadorMutanteArvore(rede1,0);
		else
			jogador1 = new JogadorAleatorio();
		if(rede2 != null)
			jogador2 = new JogadorMutanteArvore(rede2,0);
		else
			jogador2 = new JogadorAleatorio();
		for(i=0; i < NUMMAXJOGADAS; i++){
			casas = jogador1.jogar(tabuleiro.getCasas());
			if(casas == null){
				return -1;
			}
			tabuleiro.setCasas(casas);
			tabuleiro = tabuleiro.inverter();
			casas = jogador2.jogar(tabuleiro.getCasas());			
			if(casas == null){
				return 1;
			}
			tabuleiro.setCasas(casas);
			tabuleiro = tabuleiro.inverter();
		}	
		return 0;
	}

}
